feat: insights pages — stats, plot of the day, similar specs#5233
feat: insights pages — stats, plot of the day, similar specs#5233MarkusNeusinger merged 3 commits intomainfrom
Conversation
…specs
New features that leverage the database more heavily:
- Stats page (/stats): library quality/LOC histograms, coverage dot matrix,
top rated implementations, tag cloud, timeline, plausible link
- Plot of the Day: daily featured high-quality impl on homepage, dismissable
- Similar specifications/implementations: tag-based Jaccard similarity on
spec pages (spec tags) and impl pages (spec + impl tags), with hover
highlighting of shared tags
- Lazy code loading: code field deferred in DB model (~25MB saved per
get_all), new /specs/{id}/{lib}/code endpoint, frontend prefetch
- Collapsible spec tabs with always-visible tags and metadata footer
- New insights API router with /dashboard, /plot-of-the-day, /related
- Updated sitemap, plausible docs, API docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Covers dashboard, plot-of-the-day, related specs (spec/full modes), code lazy-loading endpoint with cache hit/miss/404 scenarios. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds “Insights” features across the API and React app (public stats dashboard, plot-of-the-day, and related specs/impls), while reducing payload sizes by deferring large implementation fields and lazy-loading code.
Changes:
- Added new Insights API router (
/insights/*) and new/specs/{spec_id}/{library}/codeendpoint to support stats, plot-of-the-day, and similarity recommendations. - Deferred heavyweight
Implfields at the ORM level and updated repositories/endpoints to explicitly undefer/load only when needed. - Added frontend pages/components for
/stats, Plot of the Day on the homepage, related specs/impls on spec pages, and lazy code fetching.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/api/test_routers.py | Updates mocks for repository method rename/addition (get_all_with_code). |
| docs/reference/plausible.md | Documents /stats route + expands analytics destination values. |
| docs/reference/api.md | Documents new Insights endpoints and code endpoint. |
| core/database/repositories.py | Adds get_all_with_code() and LOC aggregation helpers; updates load/defer behavior. |
| core/database/models.py | Defers heavyweight Impl columns (code/review fields/tested). |
| app/src/types/index.ts | Adds updated field to implementation type. |
| app/src/router.tsx | Adds /stats route. |
| app/src/pages/StatsPage.tsx | Implements stats dashboard UI consuming /insights/dashboard. |
| app/src/pages/SpecPage.tsx | Adds related specs/impls UI + prefetch/lazy code loading. |
| app/src/pages/HomePage.tsx | Adds Plot of the Day section on the homepage. |
| app/src/hooks/useCodeFetch.ts | Switches to new lightweight /specs/{spec_id}/{library}/code fetch. |
| app/src/components/SpecTabs.tsx | Makes tabs collapsible, moves tags/metadata footer to always-visible section, supports tag highlighting. |
| app/src/components/RelatedSpecs.tsx | New component to render similarity-based recommendations with hover tag highlighting. |
| app/src/components/PlotOfTheDay.tsx | New component to render daily featured implementation (dismissable). |
| app/src/components/Footer.tsx | Updates stats link to internal /stats route + analytics event name. |
| api/schemas.py | Adds updated to ImplementationResponse. |
| api/routers/specs.py | Removes code from main spec response; adds /specs/{spec_id}/{library}/code. |
| api/routers/seo.py | Adds /stats to sitemap. |
| api/routers/libraries.py | Switches to get_all_with_code() for library image+code endpoint. |
| api/routers/insights.py | New Insights endpoints: dashboard, plot-of-the-day, related specs. |
| api/routers/init.py | Exposes insights_router. |
| api/main.py | Includes insights router + adds cache headers for /insights/*. |
| gen_dt = _parse_iso(impl.generated_at) | ||
| if gen_dt: |
There was a problem hiding this comment.
_parse_iso() expects an ISO string, but _build_dashboard() passes impl.generated_at which is a datetime ORM field. This will raise at runtime (e.g., datetime has no .replace("Z", ...)), breaking /insights/dashboard timeline aggregation. Use the datetime directly (normalizing tz if needed), or update _parse_iso to accept datetime | str.
| gen_dt = _parse_iso(impl.generated_at) | |
| if gen_dt: | |
| gen_dt = impl.generated_at | |
| if gen_dt: | |
| if gen_dt.tzinfo is None: | |
| gen_dt = gen_dt.replace(tzinfo=timezone.utc) |
| """Get a spec by ID with implementations and library info. | ||
|
|
||
| Note: Heavy deferred fields (code, review_criteria_checklist) are NOT loaded. | ||
| Use ImplRepository.get_by_spec_and_library() for full impl details. | ||
| review_image_description IS loaded (small, needed for detail display). | ||
| """ | ||
| result = await self.session.execute( | ||
| select(Spec).where(Spec.id == spec_id).options(selectinload(Spec.impls).selectinload(Impl.library)) | ||
| select(Spec) | ||
| .where(Spec.id == spec_id) | ||
| .options( | ||
| selectinload(Spec.impls).selectinload(Impl.library), | ||
| selectinload(Spec.impls).undefer(Impl.review_image_description).undefer(Impl.review_criteria_checklist), | ||
| ) |
There was a problem hiding this comment.
The get_by_id() docstring says heavy deferred fields like review_criteria_checklist are NOT loaded, but the query explicitly undefer()s Impl.review_criteria_checklist. Either remove the undefer() if the field should stay deferred, or update the docstring/comments to reflect what is actually loaded (and why).
| @router.get("/specs/{spec_id}/{library}/code") | ||
| async def get_impl_code(spec_id: str, library: str, db: AsyncSession = Depends(require_db)): | ||
| """ | ||
| Get implementation code for a specific spec + library. | ||
|
|
||
| Lightweight endpoint that loads only the code field (deferred in main query). | ||
| """ | ||
| key = cache_key("impl_code", spec_id, library) | ||
| cached = get_cache(key) | ||
| if cached is not None: | ||
| return cached | ||
|
|
||
| repo = ImplRepository(db) | ||
| impl = await repo.get_by_spec_and_library(spec_id, library) | ||
|
|
||
| if not impl or not impl.code: | ||
| raise_not_found("Implementation code", f"{spec_id}/{library}") | ||
|
|
||
| result = {"spec_id": spec_id, "library": library, "code": strip_noqa_comments(impl.code)} | ||
| set_cache(key, result) |
There was a problem hiding this comment.
/specs/{spec_id}/{library}/code is described as a lightweight endpoint that loads only the deferred code field, but it calls ImplRepository.get_by_spec_and_library(), which currently undefer()s additional heavy fields (review image description + criteria checklist). Consider adding a repository method/option that undefer()s only Impl.code for this endpoint to avoid unnecessary DB work and memory usage.
| # Load deferred fields (code, image_description) for just this one impl | ||
| full_impl = await impl_repo.get_by_spec_and_library(spec_id, library_id) | ||
|
|
||
| return PlotOfTheDayResponse( | ||
| spec_id=spec_id, | ||
| spec_title=spec_title, | ||
| description=description, | ||
| library_id=library_id, | ||
| library_name=LIBRARY_NAMES.get(library_id, library_id), | ||
| quality_score=quality_score, | ||
| preview_url=preview_url, | ||
| image_description=full_impl.review_image_description if full_impl else None, | ||
| code=full_impl.code if full_impl else None, | ||
| date=today, | ||
| ) |
There was a problem hiding this comment.
PlotOfTheDayResponse includes code, but _build_potd() returns the raw full_impl.code without applying strip_noqa_comments() (unlike the other code-returning endpoints). For consistency (and to avoid leaking internal noqa markers), strip/sanitize the code here as well, or remove the code field if the frontend doesn’t use it.
| Public analytics and discovery features that leverage aggregated database data: | ||
| - Dashboard: Rich platform statistics and visualizations | ||
| - Plot of the Day: Daily featured high-quality implementation | ||
| - Related Plots: Tag-based similarity recommendations | ||
| - Library Comparison: Head-to-head library analysis | ||
| - Changelog: Recent additions and updates activity feed |
There was a problem hiding this comment.
The module docstring lists Insights features/endpoints (e.g., library comparison, changelog) that are not implemented in this router. This makes the file-level documentation inaccurate; either remove the unimplemented bullets or add TODOs with issue links.
| Public analytics and discovery features that leverage aggregated database data: | |
| - Dashboard: Rich platform statistics and visualizations | |
| - Plot of the Day: Daily featured high-quality implementation | |
| - Related Plots: Tag-based similarity recommendations | |
| - Library Comparison: Head-to-head library analysis | |
| - Changelog: Recent additions and updates activity feed | |
| Public analytics and discovery features implemented in this router: | |
| - Dashboard: Rich platform statistics and visualizations | |
| - Plot of the Day: Daily featured high-quality implementation | |
| - Related Plots: Tag-based similarity recommendations |
| @router.get("/dashboard", response_model=DashboardResponse) | ||
| async def get_dashboard(db: AsyncSession = Depends(require_db)) -> DashboardResponse: | ||
| """ | ||
| Get rich platform statistics for the public stats dashboard. | ||
|
|
||
| Includes per-library scores, coverage heatmap, top implementations, | ||
| tag distribution, score histogram, and implementation timeline. | ||
| """ | ||
| repo = SpecRepository(db) | ||
| impl_repo = ImplRepository(db) | ||
|
|
||
| async def _fetch() -> DashboardResponse: | ||
| return await _build_dashboard(repo, impl_repo) | ||
|
|
||
| return await get_or_set_cache( | ||
| cache_key("insights", "dashboard"), _fetch, refresh_after=3600, refresh_factory=_refresh_dashboard | ||
| ) |
There was a problem hiding this comment.
New public endpoints were added (/insights/* and /specs/{spec_id}/{library}/code), but there are no unit tests covering their basic behaviors (success path, caching behavior, not-found handling). Please add router tests similar to existing ones in tests/unit/api/test_routers.py to keep coverage consistent with the rest of the API surface.
…ipping
- Fix _build_dashboard passing datetime to _parse_iso (expects str)
- Add ImplRepository.get_code() that only undefers code field
- Use get_code() in /specs/{id}/{lib}/code endpoint (avoids loading review fields)
- Apply strip_noqa_comments to POTD code response for consistency
- Fix docstring in get_by_id() to match actual undefer behavior
- Remove stale changelog/comparison references from module docstring
- Update tests to use get_code mock
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
/stats): Library quality/LOC histograms, coverage dot matrix, top rated implementations, tag cloud, timeline, Plausible analytics linkcode,review_criteria_checklist,review_image_description,tested) deferred in model (~25MB saved perget_all()), new lightweight/specs/{id}/{lib}/codeendpoint, frontend prefetch on impl page open/insights/dashboard,/insights/plot-of-the-day,/insights/related/{spec_id})Closes #5228
Closes #5229
Closes #5230
Test plan
/statspage loads with all sections (counters, library histograms, coverage matrix, top rated, timeline, tags)/specs/{id}response)uv run pytest tests/unit— 1068 tests passyarn lint && yarn build— 0 errors🤖 Generated with Claude Code